package framework.grid;
import framework.fault.IFaultTolerantPeer;
import framework.grid.GridAlgorithm.Extent;
import framework.grid.data.INodeData;
import framework.grid.data.INodeMetaData;
import framework.grid.data.ReplicatedNodeData;
import framework.grid.data.ReplicatedNodeMetaData;
import framework.util.PeerEdge;
import framework.util.PeerNode;

import java.io.Serializable;
import java.rmi.RemoteException;



public class NodePeer implements Serializable {


	private INodeData data;
	private INodeMetaData metaData;

	//TODO change from array to single PeerMediator, save this to local variable, in order to forward
	//tasks to it and getMediator()
	public static NodePeer createInstance(String name, IFaultTolerantPeer[] replicationPeers) 
	throws RemoteException {
		NodePeer nodePeer = new NodePeer(
				ReplicatedNodeData.createInstance(replicationPeers),
				ReplicatedNodeMetaData.createInstance(replicationPeers));
		nodePeer.getMetaData().setNode(new PeerNode(name,GridAlgorithm.EMPTY_PATH,nodePeer));

		return nodePeer;
	}


	public static NodePeer copyInstance(NodePeer oldPeer,IFaultTolerantPeer[] replicationPeers) 
	throws RemoteException {
		NodePeer nodePeer = new NodePeer(
				ReplicatedNodeData.
					copyInstance((ReplicatedNodeData) oldPeer.data, replicationPeers),
				ReplicatedNodeMetaData.
					copyInstance((ReplicatedNodeMetaData) oldPeer.metaData, replicationPeers));
		
		//copy old node data
		PeerNode oldNode = nodePeer.getMetaData().getNode();
		nodePeer.getMetaData().setNode(
				new PeerNode(oldNode.getName(), oldNode.getPath(), nodePeer));
		
		//set all references from other nodes to this new node
		INodeMetaData meta = nodePeer.getMetaData();
		for (int i=0; i<(GridAlgorithm.MAX_PATH_LENGTH+2); i++) {
			PeerEdge ref = meta.getRef(i);
			if (ref!=null) {
				NodePeer targetPeer = ref.getTargetNode().getPeer();
				INodeMetaData targetMeta = targetPeer.getMetaData();
				String targetPath = ref.getPath();
				char lastBit = targetPath.charAt(targetPath.length()-1);
				String newReversePath = 
					targetPath.substring(0, targetPath.length()-1) + 
					(lastBit == GridAlgorithm.BIT_HIGH ? 
							GridAlgorithm.BIT_LOW : GridAlgorithm.BIT_HIGH);
				targetMeta.setRef(i, new PeerEdge(newReversePath, meta.getNode()));
			}
		}
		return nodePeer;
	}

	protected NodePeer(INodeData data, INodeMetaData metaData) {
		super();
		this.data = data;
		this.metaData = metaData;
	}

	public INodeMetaData getMetaData() {
		return this.metaData;
	}

	public INodeData getData() {
		return this.data;
	}

	/*
	 * CALL ALL METHODS ONLY FROM ROOT NODE
	 */
	public void join(NodePeer joinPeer) throws RemoteException {
		NodePeer minPeer = GridAlgorithm.findExtent(GridAlgorithm.EMPTY_PATH, this, null, Extent.MIN);
		GridAlgorithm.join(joinPeer, minPeer);
	}

	public void leave(NodePeer leavePeer) throws RemoteException {
		int peerLength = leavePeer.getMetaData().getNode().getPath().length();
		NodePeer maxPeer = GridAlgorithm.findExtent(GridAlgorithm.EMPTY_PATH, this, null, Extent.MAX);
		int maxPeerLength = maxPeer.getMetaData().getNode().getPath().length();

		if (peerLength < maxPeerLength) {
			GridAlgorithm.leave(maxPeer);

			GridAlgorithm.join(maxPeer, leavePeer);
		}
		GridAlgorithm.leave(leavePeer);
	}

	public String[] get(String key) throws RemoteException {
		NodePeer peer = GridAlgorithm.search(key, this);
		return peer.getData().get(key);
	}

	public void put(String key, String value) throws RemoteException{
		NodePeer peer = GridAlgorithm.search(key, this);
		peer.getData().put(key, value);
	}

	public String[] remove(String key) throws RemoteException {
		NodePeer peer = GridAlgorithm.search(key, this);
		return peer.getData().remove(key);
	}

}
